package com.klangner.go.app.strategy;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;

import com.klangner.game.ai.ab.IMoveSelector;
import com.klangner.game.board.BoardPosition;
import com.klangner.game.board.IBoard;
import com.klangner.game.board.Move;
import com.klangner.go.app.rules.BoardModel;
import com.klangner.go.app.rules.GobanLocation;
import com.klangner.go.app.rules.StonesGroupOld;


public class BasicAtariGoStrategy implements IMoveSelector{

    public Collection<Move> getCandidates( BoardModel position, int color ) {
        Vector<Move> candidates = new Vector<Move>();

        Iterator<Move> nextMoveCandidateEnumeration = contactMoves(position);
            // Select as possible next moves just the contact moves
            // Not as a best strategy :-) but as a reasonable filtering method
            //}

        if (!nextMoveCandidateEnumeration.hasNext()) {
            nextMoveCandidateEnumeration = position.legalMoves();
        }

        for ( ; nextMoveCandidateEnumeration.hasNext(); ) {
        	BoardPosition gl = nextMoveCandidateEnumeration.next();
          Move move = new Move(gl.getX(), gl.getY(), position.getTurnColor());

          candidates.addElement( move );
        }

        return candidates;
    }

    static class EstimatedMove {
        public EstimatedMove( Move move, float value ){
            move_ = move;
            value_ = value;
        }

        public String toString() {
            return "(" + move_.toString() + " : " + value_ + ")";
        }

        public Move move_;
        public float value_;
    }

    static class EstimatedMoveComparator implements Comparator {
        public int compare( Object obj1, Object obj2 ) {
            EstimatedMove move1 = (EstimatedMove)obj1;
            EstimatedMove move2 = (EstimatedMove)obj2;

            if ( move1.value_ > move2.value_ )
                return -1;
            else if ( move1.value_ == move2.value_ )
                return 0;
            else
                return 1;
        }
    }

    public Vector getSortedCandidates( BoardModel position,
                                              IGoEstimator estimator,
                                              int turn ) {
        assert( estimator != null );
        assert( position != null );

        Collection<Move> candidates = getCandidates( position, position.getTurnColor() );

        Vector estimatedCandidates = new Vector();

        Iterator<Move> it = candidates.iterator();
        while(it.hasNext()) {
        	Move move = it.next();

//            Debug.log("Start position: " + position.toString());
            BoardModel nextPosition = position.positionAfterMove( move );
//            Debug.log("Position after move " + move.toString() + " is: " + nextPosition.toString());

            estimatedCandidates.addElement(
                new EstimatedMove(move,
                                  estimator.estimatePosition(nextPosition,
                                                             turn)));
        }

        Collections.sort(estimatedCandidates, new EstimatedMoveComparator());

//        Debug.log("Sorted candidates:");
        for (int i = 0; i < estimatedCandidates.size(); ++i) {
//            Debug.log(((EstimatedMove)estimatedCandidates.elementAt( i )).toString());
        }

        Vector result = new Vector();
        for (int i = 0; i < estimatedCandidates.size(); ++i) {
            result.addElement(((EstimatedMove)estimatedCandidates.elementAt( i )).move_);
        }

        return result;
    }

    public static Move tryBasicMove(BoardModel goPosition) {
        Move theMove = null;

        if ((theMove = tryCapture(goPosition)) != null)
            return theMove;
        else if ((theMove = tryDefend(goPosition)) != null)
            return theMove;

        return theMove;
    }

        // try to capture an enemy group
    public static Move tryCapture(BoardModel goPosition) {
    		Vector<StonesGroupOld> listOfNeighborEnemies;
        Set libertiesList;

        if (goPosition.getTurnColor() == IBoard.BLACK_STONE)
            listOfNeighborEnemies = goPosition.getWhiteGroups();
        else
            listOfNeighborEnemies = goPosition.getBlackGroups();

        Enumeration enemies = listOfNeighborEnemies.elements();
        for (;enemies.hasMoreElements();) {
            libertiesList = ((StonesGroupOld)enemies.nextElement()).getLiberties();
            if (libertiesList.size() == 1) {
                GobanLocation location = (GobanLocation)(libertiesList.iterator().next());
                return new Move(location.x, location.y, goPosition.getTurnColor());
            }
        }

        return null;
    }

        // try to defend own's groups against capture
    public static Move tryDefend(BoardModel goPosition) {
    	Vector<StonesGroupOld> listOfNeighborFriends = new Vector<StonesGroupOld>();
        Set libertiesList;
        Move theMove;

        if (goPosition.getTurnColor() == IBoard.WHITE_STONE)
            listOfNeighborFriends = goPosition.getWhiteGroups();
        else
            listOfNeighborFriends = goPosition.getBlackGroups();

        Enumeration friends = listOfNeighborFriends.elements();
        for (;friends.hasMoreElements();) {
            libertiesList = ((StonesGroupOld)friends.nextElement()).getLiberties();
            if (libertiesList.size() == 1) {
                GobanLocation location = (GobanLocation)(libertiesList.iterator().next());
                theMove = new Move(location.x, location.y, goPosition.getTurnColor());

                if (goPosition.isLegalMove(theMove))
                    return theMove;
            }
        }
        return null;
    }

        // returns an iterator with the possible contact moves
    public static Iterator contactMoves(BoardModel goPosition) {
        Enumeration blackGroups, whiteGroups;
        Set result, currentLiberties;
        GobanLocation currentLiberty;

        result = new HashSet();

        whiteGroups = goPosition.getWhiteGroups().elements();
        blackGroups = goPosition.getBlackGroups().elements();

            // add black groups' liberties to the list
        for (;blackGroups.hasMoreElements();) {
                // get current group's liberties
            result.addAll(((StonesGroupOld)blackGroups.nextElement()).getLiberties());
        }

            // add white groups' liberties to the list
        for (;whiteGroups.hasMoreElements();) {
                // get current group's liberties
            result.addAll(((StonesGroupOld)whiteGroups.nextElement()).getLiberties());
        }

        Vector<Move> moves = new Vector<Move>();
        Iterator<GobanLocation>	it = result.iterator();
        while( it.hasNext() ){
        	GobanLocation gl = it.next();
        	moves.add(new Move(gl.x, gl.y, goPosition.getTurnColor()));
        }
        
//        return filterSelfAtari(goPosition, filterLegalMoves(goPosition, result)).getElements();
        return filterLegalMoves( goPosition, moves.iterator() ).iterator();
    }

    public static Iterator<Move> extremeAttackDefenseMoves(BoardModel goPosition) {
        Set result = new HashSet();
        Move theMove, bestMove = null;

        Vector<StonesGroupOld> listOfEnemies;
        Vector<StonesGroupOld> listOfFriends;

        Set libertiesList;
        StonesGroupOld currentWeakest = null, currentGroup = null;
        int currentNrLiberties = 1000, bestNrliberties = -1;

        if (goPosition.getTurnColor() == IBoard.BLACK_STONE) {
            listOfEnemies = goPosition.getWhiteGroups();
            listOfFriends = goPosition.getBlackGroups();
        }
        else {
            listOfEnemies = goPosition.getBlackGroups();
            listOfFriends = goPosition.getWhiteGroups();
        }

        Enumeration enemies = listOfEnemies.elements();
        Enumeration friends = listOfFriends.elements();

            // find weakest enemy group
        for (;enemies.hasMoreElements();) {
            currentGroup = (StonesGroupOld)enemies.nextElement();
            libertiesList = currentGroup.getLiberties();

            if (libertiesList.size() < currentNrLiberties) {
                currentNrLiberties = libertiesList.size();
                currentWeakest = currentGroup;
            }
        }

        if (currentWeakest != null)
                // append opponent's weakeast group's liberties
            result.addAll(currentWeakest.getLiberties());

        currentWeakest = null;

            // find weakest own group
        for (;friends.hasMoreElements();) {
            currentGroup = (StonesGroupOld)friends.nextElement();
            libertiesList = currentGroup.getLiberties();

            if (libertiesList.size() < currentNrLiberties) {
                currentNrLiberties = libertiesList.size();
                currentWeakest = currentGroup;
            }
        }

        if (currentWeakest != null)
                // append opponent's weakeast group's liberties
            result.addAll(currentWeakest.getLiberties());

        Vector<Move> moves = new Vector<Move>();
        Iterator<GobanLocation>	it = result.iterator();
        while( it.hasNext() ){
        	GobanLocation gl = it.next();
        	moves.add(new Move(gl.x, gl.y, goPosition.getTurnColor()));
        }
        
        Iterator<Move> mit = filterLegalMoves(goPosition, moves.iterator()).iterator();
        return filterSelfAtari(goPosition, mit).iterator();
    }

        // it filters the <list> of Moves (second argument), returning only those
        // Moves which are legal in the given <goPosition>
    private static Vector<Move> filterLegalMoves(BoardModel goPosition, Iterator<Move> list) {
        Vector<Move> moves = new Vector<Move>();

        int nextColor = goPosition.getTurnColor();

        while( list.hasNext()) {
        	Move currentMove = list.next();

                // only add the legal moves in the list
            if (goPosition.isLegalMove(currentMove))
                moves.add(currentMove);
        }

        return moves;
    }

        // it filters the <list> of Moves (second argument), returning only those
        // Moves which are not placing the Player to play it in Atari (i.e. worst
        // possible move in an atari-go game)
    private static Vector<Move> filterSelfAtari(BoardModel goPosition, Iterator<Move> list) {
      Vector<Move> moves = new Vector<Move>();

      int nextColor = goPosition.getTurnColor();

      while( list.hasNext()) {
      	Move currentMove = list.next();

        if (! goPosition.isSelfAtariMove(currentMove))
        	moves.add(currentMove);
      }
     
    	return moves;
    }
}
